//  Author:
//       Daniel Großer <olav.daniel.grosser@googlemail.com>
//  
//  Copyright (c) 2012 Copyright (C) 2012 by Daniel Großer. This Code is under GNU LGPL 3.0
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;

namespace Beauty
{
    /// <summary>
    /// A terminal comes at the very end of resolving grammar. Mark properties with
    /// descendants of this class where the value must directly be adopted.
    /// </summary>
    public abstract class TerminalAttribute : GrammarEntityAttribute, IComparable
    {
		/// <summary>
		/// Gets the scanner-precedence value. Terminals with a lower
		/// value will be preferred by the scanner. Among a set of
		/// equal-precedence terminals, the longest match will win.
		/// </summary>
		/// <value>
		/// The scanner precedence value.
		/// </value>
		public abstract byte ScannerPrecedence { get; }
		
        /// <summary>
        /// Set this to true if you care about the existance of the terminal, but
        /// not the value. For example, if you have a keyword-terminal, then you
        /// already know its value (the keyword) and don't need it to be assigned
        /// to your property.
        /// </summary>
        public bool Ignore { get; set; }

        /// <summary>
        /// Try to match the terminal at the very start of a string.
        /// </summary>
        /// <param name="input">The string to match.</param>
        /// <returns>A value greater 0 iff match is successful, where the return value equals the matched length.</returns>
        public abstract int MatchStart(string input);

        /// <summary>
        /// Get/Set the string that forms this terminal.
        /// </summary>
        protected string StringTerminal { get; set; }

        /// <summary>
        /// Get/Set the type of string comparison to use when matching
        /// the terminal. By default the comparison is done in a
        /// case-sensitive mannor.
        /// </summary>
        public StringComparison Comparison { get; set; }

        /// <summary>
        /// Compare this terminal to another. This comparator is used to ensure
        /// that terminal look-ahead is unique which is a precondition for LL(1)
        /// languages.
        /// </summary>
        /// <param name="obj">The object to compare to.</param>
        /// <returns>The comparison result.</returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (!(obj is TerminalAttribute))
                return false;
            return StringTerminal.Equals((obj as TerminalAttribute).StringTerminal);
        }

		#region IProduce EBNF implementation
        /// <summary>
        /// Get EBNF form.
        /// </summary>
        public string EBNF
        {
            get { return string.Format("\"{0}\"", StringTerminal); }
        }
		#endregion
		
		#region IComparable implementation
		/// <summary>
		/// Compare this instance to another instance.
		/// </summary>
		/// <returns>
		/// An ordering metrics based on <see cref="ScannerPrecedence"/> and then <see cref="StringTerminal"/>.
		/// </returns>
		/// <param name='obj'>
		/// Object to compare to.
		/// </param>
		public int CompareTo (object obj)
		{
			var other = obj as TerminalAttribute;
			
			if (other == null)
				return 1;
			
			if (ScannerPrecedence == other.ScannerPrecedence)
				return StringTerminal.CompareTo(other.ScannerPrecedence);
			else
				return ScannerPrecedence.CompareTo(other.ScannerPrecedence);	
		}
		#endregion
    }
}
